%%%-------------------------------------------------------------------
%%% File    : rdf_mapred_builder.erl
%%% Author  :  <vjache>
%%% Description : 
%%%
%%% Created : 28 Jul 2009 by  <vjache>
%%%-------------------------------------------------------------------
-module(rdf_mapred_builder).

-include("../include/rdf_record.hrl").
-include("../include/rdf_spalg.hrl").
-include("../include/iter.hrl"). 

%% API
-export([find/1,find/2]).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: 
%% Description:
%%--------------------------------------------------------------------
find(QuadPattSpec) ->
	case lookup_info(QuadPatt=normalize(QuadPattSpec)) of
		undefined ->
			throw({too_wide_quad_pattern,QuadPatt});
		{Node,Role,_Const} ->
			{Node,find_direct(Role,QuadPatt)}
	end.
find(QuadPattSpec,no_cache) ->
	find(QuadPattSpec);
find(QuadPattSpec,cache) ->
	cache_ets:obtain(
	  {?MODULE,find_cache}, QuadPattSpec,
	  fun() ->
			  Node=node(),
			  {Node,Iter}=find(QuadPattSpec),
			  {Node,iter:from_list(iter:to_list(Iter))}
	  end).
%%====================================================================
%% Internal functions
%%====================================================================
find_direct(Role,QuadPatt) ->
	QuadPattNorm=normalize(QuadPatt),
	case element(rdf_record:role_pos(Role)+1,QuadPattNorm) of
		Const when ?IS_CONST(Const) ->
			BestIndex=rdf_store:index_for(Role),
			_Iter=iter_mapper:new(
					iter_vect_query:new(BestIndex, {lookup,Const}), 
					fun(Quad) ->
							match(Quad, QuadPattNorm)
					end);
		BadConst ->
			exit({not_a_const,BadConst,at_role,Role,at_patt,QuadPattNorm})
	end.

lookup_info(QuadPatt) when is_record(QuadPatt, ?QUAD_TAG_SA) ->
	case best_lookup(QuadPatt) of
		undefined ->
			undefined;
		{Role,Const} ->
			Node=mapred:key2node(Const),
			{Node,Role,Const}
	end.

match(Q,QuadPatt) ->
	?SPOG=rdf_record:to_norm(Q),
	match(?SPOG,QuadPatt,5).
match(Quad,_QuadPatt,1) ->
	Quad;
match(Quad,QuadPatt,Pos) ->
	case element(Pos,QuadPatt) of
		Val when ?IS_VAR(Val) orelse Val=='_' ->
			match(Quad,QuadPatt,Pos-1);
		Val when ?IS_CONST(Val)->
			if 
				Val==element(Pos,Quad) ->
					match(Quad,QuadPatt,Pos-1);
				true ->
					?REJECT_REC
			end
	end.

best_lookup({?QUAD_TAG_SA, S,_P,_O,_G}) when ?IS_CONST(S)->
	{s,S};
best_lookup({?QUAD_TAG_SA, _S,_P,O,_G}) when ?IS_CONST(O)->
	{o,O};
best_lookup({?QUAD_TAG_SA, _S,_P,_O,G}) when ?IS_CONST(G)->
	{g,G};
best_lookup({?QUAD_TAG_SA, _S,P,_O,_G}) when ?IS_CONST(P)->
	{p,P};
best_lookup({?QUAD_TAG_SA, _S,_P,_O,_G}) ->
	undefined.

normalize(QuadPattProto) when is_record(QuadPattProto, ?QUAD_TAG_SA) ->
	QuadPattProto;
normalize(QuadPattProto) when tuple_size(QuadPattProto)<5 ->
	case QuadPattProto of
		{S,P,O,G} ->
			{?QUAD_TAG_SA,S,P,O,G};
		{S,P,O} ->
			{?QUAD_TAG_SA,S,P,O,'_'};
		{S,P} ->
			{?QUAD_TAG_SA,S,P,'_','_'};
		{S} ->
			{?QUAD_TAG_SA,S,'_','_','_'}
	end;
normalize(QuadPattProto) when is_list(QuadPattProto) ->
	InitList=[{1,?QUAD_TAG_SA}|[{rdf_record:role_pos(Role)+1,Val}||{Role,Val}<-QuadPattProto]],
	erlang:make_tuple(5, '_', InitList).

